using System;
using System.Collections.Generic;
using System.ComponentModel;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using FilteredStatistics.Common;
using FilteredStatistics.Controller;
using FilteredStatistics.View;

namespace FilteredStatistics.Data
{
    class DistanceLapNamedZone : INamedZone
    {
        public DistanceLapNamedZone(IActivity activity, int lapIndex, float startDistance, float endDistance, Length.Units distanceUnit)
        {
            m_ActivityCallbackHelper = new ActivityDataChangedHelper(m_Activity);
            m_ActivityCallbackHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);

            m_LapIndex = lapIndex;
            Init(activity, startDistance, endDistance, distanceUnit);
        }

        void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Activity.Duration")
            {
                m_ValidTimesDirty = true;
                TriggerValidTimesChanged();
            }
        }

#region INamedZone members

        public String Name
        {
            get
            {
                String lapName = String.Format(PluginView.GetLocalizedString("LapNumberText"), m_LapIndex + 1);

                lapName += String.Format(" [{0:0.0}; {1:0.0}]{2}", m_StartDistance, m_EndDistance, Length.LabelAbbr(m_DistanceUnit));

                return lapName;
            }
        }

        public IValueRangeSeries<DateTime> ValidTimes
        {
            get
            {
                RefreshValidTimes();

                return m_ValidTimes;
            }
        }

        public event PropertyChangedEventHandler ValidTimesChanged;

#endregion

        protected void TriggerValidTimesChanged()
        {
            if (ValidTimesChanged != null)
            {
                ValidTimesChanged(this, new PropertyChangedEventArgs("ValidTimes"));
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            DistanceLapNamedZone namedZone = obj as DistanceLapNamedZone;

            if (namedZone != null)
            {
                // Diff distance with an epsilon of 0.001 to prevent rounding errors
                float startDiff = namedZone.m_StartDistance - m_StartDistance;
                float endDiff = namedZone.m_EndDistance - m_EndDistance;

                return namedZone.m_LapIndex == m_LapIndex &&
                       Math.Abs(startDiff) < 0.001f &&
                       Math.Abs(endDiff) < 0.001f &&
                       namedZone.m_DistanceUnit == m_DistanceUnit;
            }

            return base.Equals(obj);
        }

        private void RefreshValidTimes()
        {
            if (m_ValidTimesDirty)
            {
                m_ValidTimesDirty = false;

                m_ValidTimes.Clear();

                if (Activity != null)
                {
                    ActivityInfo activityInfo = ActivityInfoCache.Instance.GetInfo(Activity);
                    DateTime startTime;
                    DateTime endTime;

                    if (activityInfo.ActualDistanceMetersTrack.Max > 0)
                    {
                        startTime = activityInfo.MovingDistanceMetersTrack.GetTimeAtDistanceMeters(Length.Convert(m_StartDistance, m_DistanceUnit, Length.Units.Meter));
                        endTime = activityInfo.MovingDistanceMetersTrack.GetTimeAtDistanceMeters(Length.Convert(m_EndDistance, m_DistanceUnit, Length.Units.Meter));
                    }
                    else
                    {
                        startTime = activityInfo.ActualTrackStart;
                        endTime = activityInfo.ActualTrackEnd;
                    }

                    m_ValidTimes.Add(new ValueRange<DateTime>(startTime, endTime));
                }

                TriggerValidTimesChanged();
            }
        }

        public void Init(IActivity activity, float startDistance, float endDistance, Length.Units distanceUnit)
        {
            m_StartDistance = startDistance;
            m_EndDistance = endDistance;
            m_DistanceUnit = distanceUnit;

            Activity = activity;
        }

        public IActivity Activity
        {
            get { return m_Activity; }
            set
            {
                if (Activity != value)
                {
                    m_Activity = value;
                    m_ActivityCallbackHelper.Activity = m_Activity;

                    m_ValidTimesDirty = true;
                    TriggerValidTimesChanged();
                }
            }
        }

        private IActivity m_Activity;
        private ActivityDataChangedHelper m_ActivityCallbackHelper = null;
        private int m_LapIndex = -1;
        private float m_StartDistance = 0;
        private float m_EndDistance = 0;
        private Length.Units m_DistanceUnit = Length.Units.Kilometer;
        IValueRangeSeries<DateTime> m_ValidTimes = new ValueRangeSeries<DateTime>();
        private bool m_ValidTimesDirty = false;
    }
}
